﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Common
{
    public class BitmapWorld
    {
        public Texture2D TextureMap { get; set; }
        public float ZeroLevel { get; set; }
        public float ElevationScale { get; set; }
        private Color[] HeightMapData { get; set; }

        /// <summary>
        /// Constructor to generate the plane geometry
        /// </summary>
        /// <param name="segments">The number of rows/columns</param>
        public BitmapWorld(Texture2D heightMap, float zeroLevel = 1, float elevationScale = 0.1f)
        {
            TextureMap = heightMap; // store the heightmap;
            HeightMapData = new Color[TextureMap.Width * TextureMap.Height];
            TextureMap.GetData<Color>(HeightMapData);
            ZeroLevel = zeroLevel;
            ElevationScale = elevationScale;
        }

        /// <summary>
        /// Performs bilinear interpolation to give the elevation at the specified (u,v)
        /// parametric position
        /// </summary>
        /// <param name="textureCoords">The parametric position on the map</param>
        /// <returns>Elevation at the point</returns>
        public float GetElevationBilinear(Vector2 textureCoords)
        {
            // Change the textureCoords from (0,1) to (0,{width;height}) range
            textureCoords *= new Vector2(TextureMap.Width - 1, TextureMap.Height - 1);
            int imageRow = (int)(textureCoords.Y);
            int imageCol = (int)(textureCoords.X);
            Vector2 uv = textureCoords - new Vector2(imageCol, imageRow);
            return ((HeightMapData[imageRow * TextureMap.Width + imageCol].R * (1 - uv.X) * (1 - uv.Y) +
                          HeightMapData[imageRow * TextureMap.Width + (imageCol + 1) % TextureMap.Width].R * uv.X * (1 - uv.Y) +
                          HeightMapData[(imageRow + 1) % TextureMap.Height * TextureMap.Width + imageCol].R * (1 - uv.X) * uv.Y +
                          HeightMapData[(imageRow + 1) % TextureMap.Height * TextureMap.Width + (imageCol + 1) % TextureMap.Width].R * uv.X * uv.Y) / 128f -
                          ZeroLevel) * ElevationScale;
        }

        /// <summary>
        /// Performs bilinear interpolation to give the elevation at the specified (u,v)
        /// parametric position
        /// </summary>
        /// <param name="textureCoords">The parametric position on the map</param>
        /// <returns>Elevation at the point</returns>
        public float GetElevationNearest(Vector2 textureCoords)
        {
            // Change the textureCoords from (0,1) to (0,{width;height}) range
            textureCoords *= new Vector2(TextureMap.Width - 1, TextureMap.Height - 1);
            int imageRow = (int)(textureCoords.Y + 0.5f);
            int imageCol = (int)(textureCoords.X + 0.5f);
            return (HeightMapData[imageRow * TextureMap.Width + imageCol].R / 128f -
                          ZeroLevel) * ElevationScale;
        }
    }
}
